use crate::vdd_prometheus::USER_COUNT_GAUGE;
use crate::{to_token, JwtClaims, WebResponseSupportTrait};
use chrono::{Local, NaiveDateTime};
use salvo::fs::NamedFile;
use salvo::http::StatusCode;
use salvo::jwt_auth::JwtAuthState;
use salvo::prelude::{JwtAuthDepotExt, Redirect, Text};
use salvo::{handler, Depot, Request, Response};
use serde::Serialize;
use serde_json::Value;
use std::any::type_name;
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::sync::{Arc, LazyLock};
use visit_dd_core::field::{ComputeAction, VddFieldType};
use visit_dd_core::i18n_text::E_ARG_BAD;
use visit_dd_core::id_factory::ID_FACTORY;
use visit_dd_core::proj::VddMessage;
use visit_dd_core::proj::APP_CONFIG;
use visit_dd_core::proj::{VddUser, VddUserRole};
use visit_dd_core::query::{VddValueEvaluation, WebQueryParams};
use visit_dd_core::record::RecordRuleDetail;
use visit_dd_core::visit::{ShareRuleOfField, ShareRuleOfModel, VisitGroupType};
use visit_dd_core::{
    datetime_format, hex, to_sha256_hash, CommonCode, ProjectRsaHelper, TYPE_DICT,
};
use visit_dd_core::{i18n, Astr, UserId};
use visit_dd_db::rust_rocksdb::WriteBatchWithTransaction;
use visit_dd_db::{log_db, message_db, record_db};
use visit_dd_db::{user_db, ROCKSDB};

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct LoginResponse {
    pub uid: UserId,
    pub uname: Astr,
    pub phone: Astr,
    pub token: Astr,
    #[serde(with = "datetime_format")]
    pub login_ts: NaiveDateTime,
    pub role: VddUserRole,
}

impl LoginResponse {
    pub fn from_user(user: &VddUser, token: &str) -> Self {
        Self {
            uid: user.id.clone(),
            uname: user.name.to_owned(),
            phone: user.phone.to_owned(),
            token: Arc::from(token),
            login_ts: Local::now().naive_local(),
            role: user.role.clone(),
        }
    }
}

/// 获取用户操作日志
#[handler]
pub(crate) async fn g_user_logs(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let j: Value = req
        .parse_body::<Value>()
        .await
        .expect(i18n!(E_ARG_BAD).as_ref());
    let query_params = WebQueryParams::try_from(&j).unwrap();
    res.ok(&log_db::select_some_by_user_id(&jwt.uid, &query_params).await);
}

/// 获取一个模型的日志
#[handler]
pub(crate) async fn g_model_logs(req: &mut Request, _depot: &mut Depot, res: &mut Response) {
    let j: Value = req
        .parse_body::<Value>()
        .await
        .expect(i18n!(E_ARG_BAD).as_ref());
    let query_params = WebQueryParams::try_from(&j).unwrap();
    res.ok(&log_db::select_some_by_model_id(&query_params).await);
}

#[handler]
pub(crate) async fn p_user_log_in(req: &mut Request, res: &mut Response) {
    let j = req
        .parse_body::<Value>()
        .await
        .expect(i18n!(E_ARG_BAD).as_ref());
    let phone = j["usr"].as_str().unwrap();
    let rsa = ProjectRsaHelper::init(&APP_CONFIG.pri_key, &APP_CONFIG.pub_key);
    let pwd = rsa.decode_b64str(j["pwd"].as_str().unwrap()).unwrap();
    let pwd = to_sha256_hash(pwd.as_bytes());
    let user = user_db::u_select_by_phone(phone).await;
    if let Some(user) = user {
        if pwd != user.password {
            res.error(i18n!(1011).as_ref());
        } else {
            let claim = JwtClaims::from_user(&user);
            let response = LoginResponse::from_user(&user, &to_token(&claim));
            let _ = user_db::u_after_login(&user.id).await;
            res.ok(&response);
        }
    } else {
        res.error(i18n!(1012).as_ref());
    }
}

/// 用户注册
#[handler]
pub(crate) async fn p_user_register(req: &mut Request, res: &mut Response) {
    let j = req.parse_body::<Value>().await.unwrap();
    if !user_db::u_check_phone_unique(j["phone"].as_str().unwrap()).await {
        res.error(i18n!(1011).as_ref());
    }
    let mut usr = VddUser::try_from(&j).unwrap();
    let user_count = user_db::u_count_all().await;
    if user_count < 1 {
        // 保证平台上第一个注册用户是管理员
        usr.role = VddUserRole::UrAdmin;
    }
    let _ = user_db::u_register(&usr).await;
    LazyLock::force(&USER_COUNT_GAUGE).set((user_count + 1) as i64);
    res.ok_empty();
}

/// 获取用户自身的信息
#[handler]
pub(crate) async fn g_about_me(_req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let user = user_db::u_load(&jwt.uid, false, false).await;
    res.ok(&user);
}

/// 修改密码
#[handler]
pub(crate) async fn p_change_password(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let j = req.parse_body::<Value>().await.unwrap();
    if j.get("oldPwd").is_none() || j.get("newPwd").is_none() {
        res.error(i18n!(E_ARG_BAD).as_ref());
        return;
    }
    let rsa = ProjectRsaHelper::init(&APP_CONFIG.pri_key, &APP_CONFIG.pub_key);
    let old_pwd = rsa.decode_b64str(j["oldPwd"].as_str().unwrap()).unwrap();
    let old_pwd = to_sha256_hash(old_pwd.as_bytes());
    let new_pwd = rsa.decode_b64str(j["newPwd"].as_str().unwrap()).unwrap();
    let new_pwd = to_sha256_hash(new_pwd.as_bytes());
    let r = user_db::u_change_password(&jwt.uid, &old_pwd, &new_pwd).await;
    res.wrap(r);
}

/// 校验jwt
#[handler]
pub(crate) async fn p_check_token(_req: &mut Request, depot: &mut Depot, res: &mut Response) {
    match depot.jwt_auth_state() {
        JwtAuthState::Authorized => {
            let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
            if Local::now().timestamp() < jwt.exp {
                let user = user_db::u_load(&jwt.uid, false, false).await;
                let claim = JwtClaims::from_user(&user);
                res.ok(&to_token(&claim));
            } else {
                res.error(i18n!(1014).as_ref());
            }
        }
        _ => {
            res.error(i18n!(1014).as_ref());
        }
    }
}

/// 获取同平台的其他用户
#[handler]
pub(crate) async fn g_others(_req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let mut users = user_db::u_all().await;
    users.retain(|user| user.id != jwt.uid);
    res.ok(&users);
}

/// 修改用户名
#[handler]
pub(crate) async fn p_change_name(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let j = req.parse_body::<Value>().await.unwrap();
    let name = j["name"].as_str().expect(i18n!(E_ARG_BAD).as_ref());
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let r = user_db::u_change_name(&jwt.uid, name).await;
    res.wrap(r);
}

/// 重新设定密钥对
#[handler]
pub(crate) async fn p_change_key(_req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let r = user_db::u_change_key(&jwt.uid).await;
    res.wrap(r);
}

/// 下载用户私钥
#[handler]
pub(crate) async fn p_download_pri_key(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let user = user_db::u_load(&jwt.uid, false, true).await;
    let pri_key = hex::decode(user.pri_key.as_bytes()).unwrap();
    let file_name = format!("{}/{}.pem", APP_CONFIG.upload_dir, user.id.0);
    let mut file = File::create(&file_name).unwrap();
    file.write_all(pri_key.as_slice()).unwrap();
    let infer = mime_infer::from_path(&file_name);
    NamedFile::builder(&file_name)
        .content_type(infer.first_or_octet_stream())
        .send(req.headers(), res)
        .await;
}

/// 获取一个用户的所有消息
#[handler]
pub(crate) async fn g_messages(_req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let mut messages = message_db::select_some_by_uid(&jwt.uid);
    messages.sort_by(|a, b| a.send_ts.cmp(&b.send_ts));
    res.ok(&messages);
}

#[handler]
pub(crate) async fn p_send_message(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let j = req.parse_body::<Value>().await.unwrap();
    if j.get("receiver").is_none() || j.get("content").is_none() {
        res.error(i18n!(E_ARG_BAD).as_ref());
        return;
    }
    let receiver = j["receiver"].as_u64().expect(i18n!(E_ARG_BAD).as_ref());
    let content = j["content"].as_str().expect(i18n!(E_ARG_BAD).as_ref());
    let msg = VddMessage::new(&jwt.uid, &UserId(receiver), content);
    res.wrap(message_db::send(&msg));
}

/// 获取当前用户的新消息计数
#[handler]
pub(crate) async fn g_new_message_count(_req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    res.ok(&message_db::count_unread_by_uid(&jwt.uid));
}

/// 设定一个用户的发件已读
#[handler]
pub(crate) async fn p_read_message(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let j = req.parse_body::<Value>().await.unwrap();
    let other_uid = j["uid"].as_u64();
    if j.get("uid").is_none() || other_uid.is_none() {
        res.error(i18n!(E_ARG_BAD).as_ref());
        return;
    }
    let other_uid = other_uid.unwrap();
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let mut batch = WriteBatchWithTransaction::<true>::default();
    message_db::update_read_ts(&mut batch, &jwt.uid, &UserId(other_uid));
    res.wrap(ROCKSDB.write(batch).map_err(|e| e.to_string().into()));
}

#[handler]
pub(crate) async fn index(res: &mut Response) {
    res.render(Redirect::found("/web/index.html"))
}

#[handler]
pub(crate) async fn upload_file(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let file = req.file("file").await;
    if let Some(file) = file {
        let file_key = ID_FACTORY.lock().unwrap().lazy_generate();
        // 检查路径是否存在,不存在则创建
        let upload_dir = &APP_CONFIG.upload_dir.to_string();
        if !fs::metadata(&upload_dir).is_ok() {
            fs::create_dir(&upload_dir).unwrap();
        }
        let dest = format!("{}/{}", upload_dir, file_key);
        let info = if let Err(e) = fs::copy(file.path(), Path::new(&dest)) {
            res.status_code(StatusCode::INTERNAL_SERVER_ERROR);
            format!("file not found in request: {e}")
        } else {
            let file_name = file.name().unwrap();
            record_db::save_upload_file_name(jwt.uid.clone(), file_key, file_name);
            format!("{}", file_key)
        };
        res.render(Text::Plain(info));
    } else {
        res.status_code(StatusCode::BAD_REQUEST);
        res.render(Text::Plain("file not found in request"));
    }
}

#[handler]
pub async fn download_file(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let jwt = &depot.jwt_auth_data::<JwtClaims>().unwrap().claims;
    let file_key = req
        .params()
        .get("file_key")
        .unwrap()
        .parse::<u64>()
        .unwrap();
    let file_name = record_db::get_upload_file_name(jwt.uid.clone(), file_key);
    let file_path = format!("{}/{}", &APP_CONFIG.upload_dir, file_key);
    let file = Path::new(&file_path);
    if file.exists() && file.is_file() {
        let infer = mime_infer::from_path(file_name.to_string());
        NamedFile::builder(file_path)
            .content_type(infer.first_or_octet_stream())
            .send(req.headers(), res)
            .await;
    } else {
        res.error(i18n!(1015).as_ref());
    }
}

/// 修改模型数据的api
#[handler]
pub async fn g_public_key(res: &mut Response) {
    let security_helper = ProjectRsaHelper::init(&APP_CONFIG.pri_key, &APP_CONFIG.pub_key);
    res.ok(&security_helper.get_pub_key());
}

#[handler]
pub async fn g_codes(req: &mut Request, res: &mut Response) {
    fn get_codes(cf: &str) -> Vec<CommonCode> {
        TYPE_DICT
            .iter()
            .map(|item| {
                if item.0 == cf {
                    Some(CommonCode {
                        code: item.1,
                        key: item.2.into(),
                        label: item.3.clone(),
                    })
                } else {
                    None
                }
            })
            .filter(|item| item.is_some())
            .map(|item| item.unwrap())
            .collect::<Vec<CommonCode>>()
    }
    let cf = req.param::<&str>("cf").unwrap();
    if cf.eq_ignore_ascii_case("VisitGroupType") {
        let codes = get_codes(type_name::<VisitGroupType>());
        res.ok(&codes);
    } else if cf.eq_ignore_ascii_case("FieldType") {
        let codes = get_codes(type_name::<VddFieldType>());
        res.ok(&codes);
    } else if cf.eq_ignore_ascii_case("VisitRight") {
        let codes = get_codes(type_name::<ShareRuleOfModel>());
        res.ok(&codes);
    } else if cf.eq_ignore_ascii_case("VisitRuleOfFields") {
        let codes = get_codes(type_name::<ShareRuleOfField>());
        res.ok(&codes);
    } else if cf.eq_ignore_ascii_case("ComputeAction") {
        let codes = get_codes(type_name::<ComputeAction>());
        res.ok(&codes);
    } else if cf.eq_ignore_ascii_case("FieldValueEvaluation") {
        let codes = get_codes(type_name::<VddValueEvaluation>());
        res.ok(&codes);
    } else if cf.eq_ignore_ascii_case("RecordValueRuleDetail") {
        let codes = get_codes(type_name::<RecordRuleDetail>());
        res.ok(&codes);
    } else {
        res.error(i18n!(1016).as_ref())
    }
}

#[handler]
pub async fn g_locale(res: &mut Response) {
    res.ok(&APP_CONFIG.lang);
}
